﻿using System;
using System.Linq;
using System.Text;
using System.Threading;

using Microsoft.VisualStudio.TestTools.UnitTesting;

namespace Centido.Core.Test
{
    /// <summary>
    ///   Restoring data testing.
    /// </summary>
    [TestClass]
    public class RestoringDataTest
    {
        /// <summary>
        ///   Domain name
        /// </summary>
        public const string DomainName = "FullAdminRights";

        /// <summary>
        ///   Domain
        /// </summary>
        private Domain _domain;

        /// <summary>
        ///   The table name.
        /// </summary>
        private string _tableName;

        /// <summary>
        ///   The table.
        /// </summary>
        private Table _table;


        /// <summary>
        ///   Initializing the test
        /// </summary>
        [TestInitialize]
        public void Initialize()
        {
            _domain = Domain.Build(DomainConfiguration.Load(DomainName));

            _domain.DeleteAll();

            _tableName = StringProperty.RandomIdentifier();
            _table = _domain.AddTable(_tableName);
            _table.GrantAll();
        }


        /// <summary>
        ///   Cleaning up after the test
        /// </summary>
        [TestCleanup]
        public void CleanUp()
        {
            _domain.DeleteAll();
        }


        /// <summary>
        ///   Backuping and restoring the current domain.
        /// </summary>
        private void BackupAndRestore()
        {
            // Creating a backup
            new BackupTask(_domain).Run();

            // Deleting all tables
            _domain.DeleteAllTables();

            Assert.AreEqual(_domain.Tables.Count, 0);

            // Restoring
            new RestoreTask(_domain).Run();
        }


        /// <summary>
        ///   Restoring table rows.
        /// </summary>
        [TestMethod]
        public void RestoringTableRows()
        {
            // Enabling the recycle bin
            _domain.HasRecycleBin = true;

            // Inserting two rows
            _table.InsertRow();
            var id = _table.InsertRow();

            // Deleting the second row
            _table.DeleteRow(id);

            Assert.AreEqual(_table.RowCount, 1);
            Assert.AreEqual(_table.InternalRowCount, 2);

            BackupAndRestore();

            var restoredTable = _domain.GetTable(_tableName);

            Assert.IsNotNull(restoredTable);

            Assert.AreEqual(restoredTable.RowCount, 1);
            Assert.AreEqual(restoredTable.InternalRowCount, 2);
        }


        /// <summary>
        ///   Restoring system columns.
        /// </summary>
        [TestMethod]
        public void RestoringSystemColumns()
        {
            // Adding another admin
            var anotherAdminName = StringProperty.RandomIdentifier();
            var anotherAdminPassword = StringProperty.RandomPassword();

            var anotherAdmin = _domain.AddUser(anotherAdminName, anotherAdminPassword);
            anotherAdmin.CanChangeSchema = true;

            // Enabling the recycle bin
            _domain.HasRecycleBin = true;

            // Connecting as the another admin
            var adminTable = _domain.Logon(anotherAdminName, anotherAdminPassword)
                .GetTable(_tableName);
            _table.GrantAll(anotherAdmin);

            // Inserting a row
            var id = adminTable.InsertRow();

            // Waiting two seconds
            Thread.Sleep(2000);

            // Updating that row
            adminTable.UpdateRow(id, _table.SystemColumns.Owner.Value("my_owner"));

            // Deleting the row
            adminTable.DeleteRow(id);

            // Reading system columns
            var createdOn = _table.SystemColumns.CreatedOn.Read(id, DataSource.InternalTable).GetDateTime();
            var createdBy = _table.SystemColumns.CreatedBy.Read(id, DataSource.InternalTable).GetString();
            var updatedOn = _table.SystemColumns.UpdatedOn.Read(id, DataSource.InternalTable).GetDateTime();
            var updatedBy = _table.SystemColumns.UpdatedBy.Read(id, DataSource.InternalTable).GetString();
            var version = _table.SystemColumns.RowVersion.Read(id, DataSource.InternalTable).GetGuid();
            var isActive = _table.SystemColumns.IsActive.Read(id, DataSource.InternalTable).GetBoolean();
            var deletedOn = _table.SystemColumns.DeletedOn.Read(id, DataSource.InternalTable).GetDateTime();
            var deletedBy = _table.SystemColumns.DeletedBy.Read(id, DataSource.InternalTable).GetString();

            BackupAndRestore();

            var restoredTable = _domain.GetTable(_tableName);

            Assert.IsNotNull(restoredTable);

            Assert.IsTrue(restoredTable.ContainsRow(id, DataSource.InternalTable));

            Assert.AreEqual(restoredTable.SystemColumns.Owner
                .Read(id, DataSource.InternalTable).GetString(), "my_owner");
            Assert.AreEqual(restoredTable.SystemColumns.RowVersion
                .Read(id, DataSource.InternalTable).GetGuid(), version);
            Assert.IsTrue(restoredTable.SystemColumns.CreatedOn
                .Read(id, DataSource.InternalTable).GetDateTime() - createdOn < TimeSpan.FromSeconds(1));
            Assert.AreEqual(restoredTable.SystemColumns.CreatedBy
                .Read(id, DataSource.InternalTable).GetString(), createdBy);
            Assert.IsTrue(restoredTable.SystemColumns.UpdatedOn
                .Read(id, DataSource.InternalTable).GetDateTime() - updatedOn < TimeSpan.FromSeconds(1));
            Assert.AreEqual(restoredTable.SystemColumns.UpdatedBy
                .Read(id, DataSource.InternalTable).GetString(), updatedBy);
            Assert.IsTrue(restoredTable.SystemColumns.DeletedOn
                .Read(id, DataSource.InternalTable).GetDateTime() - deletedOn < TimeSpan.FromSeconds(1));
            Assert.AreEqual(restoredTable.SystemColumns.DeletedBy
                .Read(id, DataSource.InternalTable).GetString(), deletedBy);
            Assert.AreEqual(restoredTable.SystemColumns.IsActive
                .Read(id, DataSource.InternalTable).GetBoolean(), isActive);
        }


        /// <summary>
        ///   Restoring a null value.
        /// </summary>
        [TestMethod]
        public void RestoringNullValue()
        {
            // Creating an integer column
            var columnName = StringProperty.RandomIdentifier();
            var column = _table.AddColumn(columnName,
                ColumnType.Integer);

            // Inserting a null value
            var id = _table.InsertRow(column.Value(null));

            BackupAndRestore();

            var restoredTable = _domain.GetTable(_tableName);

            Assert.IsNotNull(restoredTable);

            var restoredColumn = restoredTable.GetColumn(columnName);

            Assert.IsNotNull(restoredColumn);

            Assert.AreEqual(restoredColumn.Read(id).GetInteger(), null);
        }


        /// <summary>
        ///   Restoring big integer value.
        /// </summary>
        [TestMethod]
        public void RestoringBigIntValue()
        {
            // Creating a big integer column
            var columnName = StringProperty.RandomIdentifier();
            var column = _table.AddColumn(columnName,
                ColumnType.BigInteger);

            // Inserting a value
            var id = _table.InsertRow(column.Value(long.MaxValue));

            BackupAndRestore();

            var restoredTable = _domain.GetTable(_tableName);

            Assert.IsNotNull(restoredTable);

            var restoredColumn = restoredTable.GetColumn(columnName);

            Assert.IsNotNull(restoredColumn);

            Assert.AreEqual(restoredColumn.Read(id).GetBigInteger(), long.MaxValue);
        }


        /// <summary>
        ///   Restoring a binary value.
        /// </summary>
        [TestMethod]
        public void RestoringBinaryValue()
        {
            // Creating a binary column
            var columnName = StringProperty.RandomIdentifier();
            var column = _table.AddColumn(columnName,
                ColumnType.Binary);


            // Inserting a value
            var id = _table.InsertRow(column.Value(new byte[] { 1, 2, 201 }));

            BackupAndRestore();

            var restoredTable = _domain.GetTable(_tableName);

            Assert.IsNotNull(restoredTable);

            var restoredColumn = restoredTable.GetColumn(columnName);

            Assert.IsNotNull(restoredColumn);

            var restoredValue = restoredColumn.Read(id).GetBinary();
            Assert.IsTrue(restoredValue.SequenceEqual(new byte[] { 1, 2, 201 }));
        }


        /// <summary>
        ///   Restoring large binary value.
        /// </summary>
        [TestMethod]
        public void RestoringLargeBinaryValue()
        {
            // Creating a binary column
            var columnName = StringProperty.RandomIdentifier();
            var column = _table.AddColumn(columnName,
                ColumnType.Binary);

            // Creating a binary value
            var binaryValue = new byte[10000];

            new Random(Environment.TickCount).NextBytes(binaryValue);

            // Inserting a value
            var id = _table.InsertRow(column.Value(binaryValue));

            BackupAndRestore();

            var restoredTable = _domain.GetTable(_tableName);

            Assert.IsNotNull(restoredTable);

            var restoredColumn = restoredTable.GetColumn(columnName);

            Assert.IsNotNull(restoredColumn);

            var restoredValue = restoredColumn.Read(id).GetBinary();
            Assert.IsTrue(restoredValue.SequenceEqual(binaryValue));
        }


        /// <summary>
        ///   Restoring a boolean value.
        /// </summary>
        [TestMethod]
        public void RestoringBoolValue()
        {
            // Creating a boolean column
            var columnName = StringProperty.RandomIdentifier();
            var column = _table.AddColumn(columnName,
                ColumnType.Boolean);

            // Inserting a value
            var id = _table.InsertRow(column.Value(true));

            BackupAndRestore();

            var restoredTable = _domain.GetTable(_tableName);

            Assert.IsNotNull(restoredTable);

            var restoredColumn = restoredTable.GetColumn(columnName);

            Assert.IsNotNull(restoredColumn);

            Assert.AreEqual(restoredColumn.Read(id).GetBoolean(), true);
        }


        /// <summary>
        ///   Restoring a datetime value.
        /// </summary>
        [TestMethod]
        public void RestoringDateTimeValue()
        {
            // Creating a datetime column
            var columnName = StringProperty.RandomIdentifier();
            var column = _table.AddColumn(columnName,
                ColumnType.DateTime);

            // Inserting a value
            var value = new DateTime(2011, 2, 3, 12, 34, 12);
            var id = _table.InsertRow(column.Value(value));

            BackupAndRestore();

            var restoredTable = _domain.GetTable(_tableName);

            Assert.IsNotNull(restoredTable);

            var restoredColumn = restoredTable.GetColumn(columnName);

            Assert.IsNotNull(restoredColumn);

            Assert.AreEqual(restoredColumn.Read(id).GetDateTime(), value);
        }


        /// <summary>
        ///   Restoring decimal value.
        /// </summary>
        [TestMethod]
        public void RestoringDecimalValue()
        {
            // Creating a decimal column
            var columnName = StringProperty.RandomIdentifier();
            var column = (DecimalColumn)_table.AddColumn(columnName,
                ColumnType.Decimal);
            column.Precision = 10;
            column.Scale = 2;

            // Inserting a value
            var id = _table.InsertRow(column.Value(455656.23M));

            BackupAndRestore();

            var restoredTable = _domain.GetTable(_tableName);

            Assert.IsNotNull(restoredTable);

            var restoredColumn = restoredTable.GetColumn(columnName);

            Assert.IsNotNull(restoredColumn);

            Assert.AreEqual(restoredColumn.Read(id).GetDecimal(), 455656.23M);
        }


        /// <summary>
        ///   Restoring a guid value.
        /// </summary>
        [TestMethod]
        public void RestoringGuidValue()
        {
            // Creating a guid column
            var columnName = StringProperty.RandomIdentifier();
            var column = _table.AddColumn(columnName,
                ColumnType.Guid);

            // Inserting a value
            var value = Guid.NewGuid();
            var id = _table.InsertRow(column.Value(value));

            BackupAndRestore();

            var restoredTable = _domain.GetTable(_tableName);

            Assert.IsNotNull(restoredTable);

            var restoredColumn = restoredTable.GetColumn(columnName);

            Assert.IsNotNull(restoredColumn);

            Assert.AreEqual(restoredColumn.Read(id).GetGuid(), value);
        }


        /// <summary>
        ///   Restoring an integer value.
        /// </summary>
        [TestMethod]
        public void RestoringIntegerValue()
        {
            // Creating an integer column
            var columnName = StringProperty.RandomIdentifier();
            var column = _table.AddColumn(columnName,
                ColumnType.Integer);

            // Inserting a value
            const int value = int.MaxValue;
            var id = _table.InsertRow(column.Value(value));

            BackupAndRestore();

            var restoredTable = _domain.GetTable(_tableName);

            Assert.IsNotNull(restoredTable);

            var restoredColumn = restoredTable.GetColumn(columnName);

            Assert.IsNotNull(restoredColumn);

            Assert.AreEqual(restoredColumn.Read(id).GetInteger(), value);
        }


        /// <summary>
        ///   Restoring a small integer value.
        /// </summary>
        [TestMethod]
        public void RestoringSmallIntegerColumn()
        {
            // Creating a small integer column
            var columnName = StringProperty.RandomIdentifier();
            var column = _table.AddColumn(columnName,
                ColumnType.SmallInteger);

            // Inserting a value
            const short value = short.MinValue;
            var id = _table.InsertRow(column.Value(value));

            BackupAndRestore();

            var restoredTable = _domain.GetTable(_tableName);

            Assert.IsNotNull(restoredTable);

            var restoredColumn = restoredTable.GetColumn(columnName);

            Assert.IsNotNull(restoredColumn);

            Assert.AreEqual(restoredColumn.Read(id).GetSmallInteger(), value);
        }


        /// <summary>
        ///   Restoring a string value.
        /// </summary>
        [TestMethod]
        public void RestoringStringValue()
        {
            // Creating a string column
            var columnName = StringProperty.RandomIdentifier();
            var column = _table.AddColumn(columnName,
                ColumnType.String);

            // Inserting a value
            var value = StringProperty.RandomIdentifier();
            var id = _table.InsertRow(column.Value(value));

            BackupAndRestore();

            var restoredTable = _domain.GetTable(_tableName);

            Assert.IsNotNull(restoredTable);

            var restoredColumn = restoredTable.GetColumn(columnName);

            Assert.IsNotNull(restoredColumn);

            Assert.AreEqual(restoredColumn.Read(id).GetString(), value);
        }


        /// <summary>
        ///   Restoring a text value.
        /// </summary>
        [TestMethod]
        public void RestoringTextValue()
        {
            // Creating a text column
            var columnName = StringProperty.RandomIdentifier();
            var column = _table.AddColumn(columnName,
                ColumnType.Text);

            // Inserting a value
            var value = StringProperty.RandomIdentifier();
            var id = _table.InsertRow(column.Value(value));

            BackupAndRestore();

            var restoredTable = _domain.GetTable(_tableName);

            Assert.IsNotNull(restoredTable);

            var restoredColumn = restoredTable.GetColumn(columnName);

            Assert.IsNotNull(restoredColumn);

            Assert.AreEqual(restoredColumn.Read(id).GetString(), value);
        }


        /// <summary>
        ///   Restoring large text value.
        /// </summary>
        [TestMethod]
        public void RestoringLargeTextValue()
        {
            // Creating a text column
            var columnName = StringProperty.RandomIdentifier();
            var column = _table.AddColumn(columnName,
                ColumnType.Text);

            // Creating a text value
            var builder = new StringBuilder();
            var rand = new Random(Environment.TickCount);

            for (var i = 0; i < 10000; i++)
                builder.Append((char)(rand.Next() % 255));

            // Inserting a value
            var value = builder.ToString();
            var id = _table.InsertRow(column.Value(value));

            BackupAndRestore();

            var restoredTable = _domain.GetTable(_tableName);

            Assert.IsNotNull(restoredTable);

            var restoredColumn = restoredTable.GetColumn(columnName);

            Assert.IsNotNull(restoredColumn);

            Assert.AreEqual(restoredColumn.Read(id).GetString(), value);
        }


        /// <summary>
        ///   Restoring an enumeration value.
        /// </summary>
        [TestMethod]
        public void RestoringEnumValue()
        {
            // Creating a enum column
            var columnName = StringProperty.RandomIdentifier();
            var column = (EnumColumn)_table.AddColumn(columnName,
                ColumnType.Enumeration);

            column.AddMember(5);

            // Inserting a value
            const int value = 5;
            var id = _table.InsertRow(column.Value(value));

            BackupAndRestore();

            var restoredTable = _domain.GetTable(_tableName);

            Assert.IsNotNull(restoredTable);

            var restoredColumn = restoredTable.GetColumn(columnName);

            Assert.IsNotNull(restoredColumn);

            Assert.AreEqual(restoredColumn.Read(id).GetInteger(), value);
        }


        /// <summary>
        ///   Restoring a reference value.
        /// </summary>
        [TestMethod]
        public void RestoringRefValue()
        {
            // Creating a reference column
            var columnName = StringProperty.RandomIdentifier();
            var column = (RefColumn)_table.AddColumn(columnName,
                ColumnType.Reference);

            column.AddTarget(_table).HasCascadeDelete = true;

            // Inserting a row
            var refId = _table.InsertRow();

            // Inserting a value
            var id = _table.InsertRow(column.Value(refId));

            BackupAndRestore();

            var restoredTable = _domain.GetTable(_tableName);

            Assert.IsNotNull(restoredTable);

            var restoredColumn = restoredTable.GetColumn(columnName);

            Assert.IsNotNull(restoredColumn);

            Assert.AreEqual(restoredColumn.Read(id).GetGuid(), refId);

            // Deleting the row
            restoredTable.DeleteRow(refId);

            Assert.AreEqual(restoredTable.RowCount, 0);
        }
    }
}
